/*! \file syscall.c 
 *	This files holds the implementations of all system calls.
 */

#define INT_STRINGIFY(x)	#x
#define INT_TOSTRING(x)		INT_STRINGIFY(x)
#define FILE_AND_LINE		__FILE__ ":" INT_TOSTRING(__LINE__)	
	
#define TRACE(x)	{kprints("TRACE: " FILE_AND_LINE ":\t " #x "\n");}
#define DEBUG(x)	{kprints("DEBUG: " FILE_AND_LINE ":\t (" #x ") = 0x"); kprinthex((long)(x)); kprints("\n");}
#define FAIL(x)		{kprints("FAIL:  " FILE_AND_LINE ":\t (" #x ") = 0x"); kprinthex((long)(x));}

#define ENSURE(x,code)			{ if (!(x)) {FAIL(x); kprints("\n"); code} }
#define MENSURE(x,msg,code)		{ if (!(x)) {FAIL(x); kprints(" Reason: " msg);  kprints("\n"); code} }

#include "kernel.h"


	case SYSCALL_VERSION:
{
	SYSCALL_ARGUMENTS.rax = KERNEL_VERSION;
	break;
}

	case SYSCALL_PRINTS:
{
	kprints((char*) (SYSCALL_ARGUMENTS.rdi));
	SYSCALL_ARGUMENTS.rax = ALL_OK;
	break;
}

	case SYSCALL_PRINTHEX:
{
	kprinthex(SYSCALL_ARGUMENTS.rdi);
	SYSCALL_ARGUMENTS.rax = ALL_OK;
	break;
}

	case SYSCALL_DEBUGGER:
{
	/* Enable the bochs iodevice and force a return to the debugger. */
	outw(0x8a00, 0x8a00);
	outw(0x8a00, 0x8ae0);

	SYSCALL_ARGUMENTS.rax = ALL_OK;
	break;
}

	/**
	 *	The terminate system call terminates the currently running thread. The system call does 
	 *	not take any parameters and will never return to the caller. It does thus not return any 
	 *	values. Terminates the process when there are no threads left.
	 */
	case SYSCALL_TERMINATE:
{
//	TRACE("Entering SYSCALL_TERMINATE");
	int pid = thread_table[current_thread].data.owner; // The pid of the owner process of the thread.

	/* Terminate currently running thread */
	thread_table[current_thread].data.owner = -1;	// The thread is now free to be allocated again.

	/* Terminate the calling process if needed */
	process_table[pid].threads--;					// One thread of the process is removed

	/* If the process has no more running threads, clean it up */
	if (process_table[pid].threads == 0) {
		const int ppid = process_table[pid].parent;
		process_table[pid].parent = 0;
		cleanup_process(pid);
		pid = ppid;		// Parent process must have threads to execute below
	}

	/* Find thread to execute. 
	 * XXX: We should really invoke the scheduler instead.
	 */
	int i;
	for(i = 0; i < MAX_NUMBER_OF_THREADS; i++) {
		if (thread_table[i].data.owner == pid) {	// linear search OK for now
			current_thread = i;
			break;
		}
	}
	break;
}
	
	
	
	/**
	 *	The createprocess system call takes a parameter in the rdi register . This parameter is an 
	 *	integer which corresponds to an executable program. The program will be be started as a 
	 *	process by the system call. The system call returns in the rax register a value of ALL_OK 
	 *	if a process was successfully created and run. A value of ERROR is returned if any error 
	 *	occurred which prevented a new process to be created or run.
	 */	
	case SYSCALL_CREATEPROCESS:
{
//	TRACE("Entering SYSCALL_CREATEPROCESS");

	const long rdi = SYSCALL_ARGUMENTS.rdi; // The index of the program we want to start
	int new_pid = 0;
	struct prepare_process_return_value newproc;

	/* Do this when condition in MENSURE is false */
	#define CREATEPROCESS_ERROR	{SYSCALL_ARGUMENTS.rax = ERROR; break;}	
	
	/* Find first available PID. This must be when a process has no running threads */
	for (new_pid = 0; new_pid < MAX_NUMBER_OF_PROCESSES; new_pid++) {	// linear search
		if (process_table[new_pid].threads == 0)
			break;
	}
	MENSURE(new_pid < MAX_NUMBER_OF_PROCESSES, "All PIDs occupied", CREATEPROCESS_ERROR);
	
	/* Load program into memmory */
	newproc = prepare_process(executable_table[rdi].elf_image, new_pid, executable_table[rdi].memory_footprint_size);
	MENSURE(newproc.first_instruction_address != 0, "prepare_process failed",  CREATEPROCESS_ERROR);
//	MENSURE(newproc.page_table_address != 0,		"prepare_process failed",  CREATEPROCESS_ERROR);	// XXX: When we implement VM

	/* Set the parent of the new process to be the calling process.
	 * XXX: When we (eventually) support multi-threading, increment .threads rather than setting to 1.
	 */
	MENSURE(current_thread < MAX_NUMBER_OF_THREADS, "Too many threads", CREATEPROCESS_ERROR);
	const int parent_pid = thread_table[current_thread].data.owner;	// Get PID of calling thread
	process_table[new_pid].parent = parent_pid;		// We are the parent to the new process
	process_table[new_pid].threads = 1;				// Make new process valid (by having non-zero threads)

	/* Finally, try to create the actual thread. Check if thread could be allocated. */
	int new_thread = allocate_thread();
	MENSURE(new_thread != -1, "Could not allocate thread", CREATEPROCESS_ERROR);
	MENSURE(new_thread < MAX_NUMBER_OF_THREADS, "Should not happen. Error in allocate_thread", CREATEPROCESS_ERROR);

	/* Store information into the new thread.
	 * Thread must start executing the program image.
	 */
	thread_table[new_thread].data.registers.integer_registers.rflags = 0;
	thread_table[new_thread].data.owner = new_pid; // The new process owns the new thread.
	thread_table[new_thread].data.registers.integer_registers.rip = newproc.first_instruction_address;

	/* Assign the return value of the system call to rax in the callers context. */
	SYSCALL_ARGUMENTS.rax = ALL_OK;		// We got this far, no MENSURE errors

	/* Switch to the new thread and let it execute. */
	current_thread = new_thread;	// Kernel will execute current_thread
	
	break;
}


